#include "../DataModule.h"

#include <vector>

#define NUM_SPRITE_SETS		463
#define NUM_PALS		8

struct Sprite {
  private:
    uint addr, width, height;
    uchar* image;
  public:
    Sprite(uint newWidth, uint newHeight);
    Sprite(uint newAddr, uint newWidth, uint newHeight, Rom& rom);
    ~Sprite();

    uint getAddr();
    uint getWidth();
    uint getHeight();
    void getAsString(char* buf);
    void setAsString(const char* str);
    uint writeToRom(uint addr, Rom& rom);
    uint getRomSize();
};

// Used as a pointer to a Sprite from a SpriteSet
class SpritePointer {
  private:
    uint sprite;
    bool flipped; 
  public:
    SpritePointer();
    SpritePointer(uint sprite, bool flipped);
    void setSprite(uint sprite);
    uint getSprite();
    void setFlipped(bool flipped);
    bool isFlipped();
    // Load from ROM
    void load(uint addr, uint width, uint height, std::vector<Sprite*> &sprites, Rom &rom);
    uint getPointer(std::vector<Sprite*> &sprites);
};

struct SpriteSet {
  SpriteSet();
  ~SpriteSet();

  uchar width, height, palette;
  uchar unknown[5];
  int num_sprites;

  SpritePointer* pointers;

  // Returns the length of this SPT entry when it is written to the rom
  uint getRomLength();
  void writeToRom(uint addr, std::vector<Sprite*> &sprites, Rom &rom);
};

class SpritePalette {
  private:
    Color colors[16];
  public:
    void readFromRom(uint addr, Rom &rom);
    void writeToRom(uint addr, Rom &rom);
    void getAsString(char* out);
    void setAsString(const char* str);
};

class BankGroup {
  private:
    std::vector<Sprite*> bank;
    uint size;
  public:
    BankGroup();
    uint getSize();
    bool sharesSprites(SpriteSet& ss, std::vector<Sprite*> &sprites);
    bool hasSprite(Sprite* sprite);
    void addSpriteSet(SpriteSet& ss, std::vector<Sprite*> &sprites);
    void sortDescending();
    std::vector<Sprite*>* getVector();
};

class Bank {
  private:
    std::vector<Sprite*> bank;
    uint addr, free;
  public:
    Bank();
    Bank(uint addr, uint capacity);
    void init(uint addr, uint capacity);
    bool hasRoomFor(BankGroup* bg);
    void add(BankGroup* bg);
    void writeToRom(Rom &rom);
};

class SpriteModule: public DataModule {
  private:
    SpriteSet spriteSets[NUM_SPRITE_SETS];
    std::vector<Sprite*> sprites;
    SpritePalette palettes[NUM_PALS];
  public:
    ~SpriteModule();
    const char* getName();
    const char* getCredits();

    void readFromRom(Rom& rom);
    void writeToRom(Rom& rom);
    void readFromDat(char* dirName, std::map<std::string, std::string> &fns, std::map<std::string, int> &labels);
    void writeToDat(char* dirName, std::map<std::string, std::string> &fns);
};

